package com.xingbg.projectimpl.config.bean;

import com.xingbg.projectimpl.config.BeanOverride;
import com.xingbg.projectimpl.config.utils.ClassUtils;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Set;

@Slf4j
public class LiskovBeanProessor implements BeanFactoryPostProcessor {

    private String[] basePackages;

    public LiskovBeanProessor(String[] basePackages) {
        this.basePackages = basePackages;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

        Reflections reflections = new Reflections(basePackages);

        /**获取所有类上标有@BeanOverride，标识可能是一个覆盖主干产品的定制服务类*/
        Set<Class<?>> allOverrideBeanTypes = reflections.getTypesAnnotatedWith(BeanOverride.class);

        String[] beanDefinitionNames = configurableListableBeanFactory.getBeanDefinitionNames();
        for (Class<?> overrideClass : allOverrideBeanTypes) {
            Class<?> superClass = overrideClass.getSuperclass();
            for (String beanDefinitionName : beanDefinitionNames) {
                BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(beanDefinitionName);
                if (superClass.getName().equals(beanDefinition.getBeanClassName())) {
                    /**overrideClass替换superClass前，需要校验overrideClass是否都是覆盖了接口方法，而不是superClass申明方法，仅面向接口扩展，禁止滥用*/
                    String checkResult = this.checkOverrideClassSpecification(overrideClass);
                    if (checkResult != null && !"".equals(checkResult)) {
//                        throw new RuntimeException("类：" + overrideClass + "不允许重写父类的非接口方法[" + checkResult + "]");
                    }
                    //todo:如果2个子类会报错。这种情况应该报错
                    beanDefinition.setBeanClassName(overrideClass.getName());
                }
            }
        }

    }

    /**
     * 覆盖类方法定义是否符合规范
     * todo: 正确性待校验
     * @param clazz
     * @return
     */
    private String checkOverrideClassSpecification(Class clazz){
        StringBuilder result = new StringBuilder();
        /**获取覆盖类所有实现接口已申明的方法列表*/
        Collection<Method> allInterfaceDeclaredMthodList = ClassUtils.getAllInterfaceMethods(clazz);
        /**获取覆盖类的所有申明方法列表*/
        Method[] overrideMethods = clazz.getDeclaredMethods();
        /**检查所有已申明方法是否是接口定义方法，有一个不满足即抛出异常*/
        for(Method currentDeclaredMethod : overrideMethods){
            boolean specificationFlag = false;
            for(Method interfaceDeclaredMethod : allInterfaceDeclaredMthodList){
                if(ClassUtils.isOverrideMethod(currentDeclaredMethod,interfaceDeclaredMethod)){
                    specificationFlag = true;
                    break;
                }
            }
            /**如果当前类定义的方法不是覆盖接口上的方法，则直接返回不符合规范*/
            if(!specificationFlag){
                result.append(currentDeclaredMethod.toString()).append("|");
            }
        }
        if(result.length()>0){
            result = result.deleteCharAt(result.length()-1);
        }
        return result.toString();
    }
}
